﻿using System;
using Xunit;

namespace dogd.Tests
{
    [Table("Persisted")]
    public class PersistableNoPrimaryKeyClass
    {
        [Column("PersistID")]
        public int DbKey { get; set; }

        [Column("PersistScore")]
        public int Value { get; set; }
    }

	[Table("Persisted")]
	public class PersistableClass
	{
		[PrimaryKeyColumn("PersistID")]
		public int DbKey { get; set; }

		[LookupColumn("PersistName")]
		public string Name { get; set; }

		[IntegerColumn("PersistScore")]
		public int Value { get; set; }
	}

	public static class TestableStatementGenerator
	{
		public static SqlStatementGenerator Create()
		{
			var metadata = new TableMetadata(typeof(PersistableClass));
			return new SqlStatementGenerator(metadata);
		}
	}

	public class SqlStatementGeneratorFacts
	{
		public class TheSelectAllMethod
		{
			[Fact]
			public void IncludesAllAttributedProperties()
			{
				var generator = TestableStatementGenerator.Create();

				string sql = generator.SelectAll();

				Assert.Equal("SELECT PersistID, PersistName, PersistScore FROM Persisted", sql);
			}
		}

		public class TheSelectMethod
		{
			[Fact]
			public void IncludesWhereWhenLookupColumnIdentified()
			{
				var generator = TestableStatementGenerator.Create();

				string sql = generator.Select();

				Assert.Equal("SELECT PersistID, PersistName, PersistScore FROM Persisted WHERE PersistName = @Name", sql);
			}

			[Fact]
			public void AddsAndToWhereForTwoFilters()
			{
				var generator = TestableStatementGenerator.Create();

				FilterByCollection filters = new FilterByCollection();

				filters.Add(new FilterBy("PersistName", "Name"));
				filters.Add(new FilterBy("PersistScore", "Score"));

				string sql = generator.Select(filters);

				Assert.Equal("SELECT PersistID, PersistName, PersistScore FROM Persisted WHERE PersistName = @Name AND PersistScore = @Score", sql);
			}

			[Fact]
			public void ThrowsExceptionIfClassOmitsLookupAttribute()
			{
				var metadata = new TableMetadata(typeof(PersistableNoPrimaryKeyClass));
				var generator = new SqlStatementGenerator(metadata);

				Assert.Throws<NotSupportedException>(() =>
						generator.Select());
			}
		}

		public class TheInsertMethod
		{
			[Fact]
			public void IncludesAllAttributedColumnNamesAndPropertyNames()
			{
				var generator = TestableStatementGenerator.Create();

				string output = generator.Insert();

				Assert.Equal("INSERT INTO Persisted ( PersistName, PersistScore ) VALUES ( @Name, @Value ); SELECT last_insert_rowid() ", output);
			}
		}

		public class TheUpdateMethod
		{
			[Fact]
			public void IncludeAllAttributedColumnsPropertiesAndWhere()
			{
				var generator = TestableStatementGenerator.Create();

				string output = generator.Update();

				Assert.Equal("UPDATE Persisted SET PersistName = @Name, PersistScore = @Value WHERE PersistID = @DbKey", output);
			}

			[Fact]
			public void ThrowsExceptionIfClassOmitsPrimaryKeyAttribute()
			{
				var metadata = new TableMetadata(typeof(PersistableNoPrimaryKeyClass));
				var generator = new SqlStatementGenerator(metadata);

				Assert.Throws<NotSupportedException>(() =>
						generator.Update());

			}
		}


		//[Fact]
		//public void SqlStatementGenerator_Search_IncludesOnlyTextFields()
		//{
		//    var metadata = new TableMetadata(typeof(PersistableClass));
		//    var generator = new SqlStatementGenerator(metadata);

		//    var result = generator.Search("foo");

		//    Assert.Equal("SELECT PersistID, PersistName, PersistScore FROM Persisted WHERE PersistName LIKE '%foo%'", result);
		//}

		public class TheDeleteMethod
		{
			[Fact]
			public void ByPrimaryKeyThrowsExceptionIfClassOmitsPrimaryKeyAttribute()
			{
				var metadata = new TableMetadata(typeof(PersistableNoPrimaryKeyClass));
				var generator = new SqlStatementGenerator(metadata);

				Assert.Throws<NotSupportedException>(() =>
						generator.Delete(
							DeleteOption.ByPrimaryKey));
			}

			[Fact]
			public void ByPrimaryKeyIncludesWhereClause()
			{
				var generator = TestableStatementGenerator.Create();

				string sql = generator.Delete(DeleteOption.ByPrimaryKey);

				Assert.Equal("DELETE FROM Persisted  WHERE PersistID = @DbKey", sql);
			}

			[Fact]
			public void ReplacesDefaultWhereClauseWithSpecifiedFilter()
			{
				var generator = TestableStatementGenerator.Create();

				FilterByCollection filters = new FilterByCollection();
				filters.Add(new FilterBy("PersistScore", "Value"));

				string sql = generator.Delete(filters);

				Assert.Equal("DELETE FROM Persisted  WHERE PersistScore = @Value", sql);
			}
		}

		public class TheOrderByMethod
		{
			[Fact]
			public void ReturnsClauseContainingColumnAndSortOrder()
			{
				var generator = TestableStatementGenerator.Create();

				string output = SqlStatementGenerator.OrderBy(new OrderBy() { ColumnName = "Score", Order = SortOrder.Descending });

				Assert.Equal(" ORDER BY Score DESC", output);
			}
		}

		public class TheCountMethod
		{
			[Fact]
			public void ReturnsCountOfStarForNoLookup()
			{
				var generator = TestableStatementGenerator.Create();

				string sql = generator.Count(null, null);
				const string expected = "SELECT COUNT(*) FROM Persisted ";

				Assert.Equal(expected, sql);
			}

			[Fact]
			public void ReturnsCountOfColumnNameForLookup()
			{
				var generator = TestableStatementGenerator.Create();

				string sql = generator.Count();
				const string expected = "SELECT COUNT(PersistName) FROM Persisted ";

				Assert.Equal(expected, sql);
			}
		}
    }
}
